package com.omnisyr.paperanalyser.common;

import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.RandomAccess;

public class SimpleArrayList<E> implements Iterable<E>, RandomAccess {

	private int size;
	private E[] contents;

	public int size() {

		return this.size;

	}

	public boolean isEmpty() {

		return this.size < 1;

	}

	@SuppressWarnings("unchecked")
	public SimpleArrayList<E> add(E element) {

		if (this.size == 0) {

			this.contents = (E[]) new Object[10];

		}

		if (this.size >= this.contents.length) {

			E[] oldArray = this.contents.clone();

			this.contents = Arrays.copyOf(oldArray, this.ensureSize(size + 1));

		}

		this.contents[this.size++] = element;

		return this;

	}

	private int ensureSize(int minSize) {

		int newSize = this.size + (this.size >> 1);

		return minSize > newSize ? minSize : newSize;

	}

	public void set(int index, E value) {

		this.contents[index] = value;

	}

	public E get(int index) {

		return this.contents[index];

	}

	@SuppressWarnings("unchecked")
	public E[] toArray(E[] e) {

		if (this.contents == null) {

			return e;

		}

		return (E[]) Arrays.copyOf(this.contents, this.size, e.getClass());

	}

	public void sort(Comparator<? super E> comparator) {

		this.quickSort(this.contents, 0, this.size - 1, comparator);

	}

	public int search(E e, Comparator<? super E> comparator) {

		return this.search(0, this.size - 1, e, comparator);

	}

	public int search(int start, int end, E e, Comparator<? super E> comparator) {

		return this.specilSearch(start, end, e, (SpecialComparator<? super E, ? super E>) comparator::compare);

	}

	public <O> int specilSearch(O o, SpecialComparator<? super O, ? super E> comparator) {

		return this.specilSearch(0, this.size - 1, o, comparator);

	}

	public <O> int specilSearch(int start, int end, O o, SpecialComparator<? super O, ? super E> comparator) {

		while (start <= end) {

			int mid = start + (end - start >> 1);
			int compareResult = comparator.compare(o, this.contents[mid]);

			if (compareResult < 0) {

				end = mid - 1;

				continue;

			}

			if (compareResult > 0) {

				start = mid + 1;

				continue;

			}

			if (compareResult == 0) {

				return mid;

			}

		}

		return -1;

	}

	private E[] quickSort(E[] array, int left, int right, Comparator<? super E> comparator) {

		if (left >= right) {

			return array;

		}

		if (left + 10 > right) {

			return this.insertionSort(array, left, right, comparator);

		}

		this.swap(array, left, this.getMedian(array, left, left + (right - left) / 2, right, comparator));

		int partitionIndex = this.partition(array, left, right, comparator);

		this.quickSort(array, left, partitionIndex - 1, comparator);
		this.quickSort(array, partitionIndex + 1, right, comparator);

		return array;

	}

	private E[] insertionSort(E[] array, int left, int right, Comparator<? super E> comparator) {

		for (int i = left + 1; i <= right; i++) {

			for (int j = i; j > left && comparator.compare(array[j], array[j - 1]) < 0; j--) {

				this.swap(array, j, j - 1);

			}

		}

		return array;

	}

	private int getMedian(E[] array, int a, int b, int c, Comparator<? super E> comparator) {

		boolean flag1 = comparator.compare(array[a], array[b]) < 0;
		boolean flag2 = comparator.compare(array[b], array[c]) < 0;

		if (flag1 ^ flag2) {

			boolean flag3 = comparator.compare(array[a], array[c]) < 0;

			if (flag1 ^ flag3) {

				return a;

			}

			return c;

		}

		return b;

	}

	private int partition(E[] array, int left, int right, Comparator<? super E> comparator) {

		int i = left;
		int j = right + 1;

		while (true) {

			while (comparator.compare(array[++i], array[left]) < 0) {

				if (i == right) {

					break;

				}

			}

			while (comparator.compare(array[--j], array[left]) > 0) {

				if (j == left) {

					break;

				}

			}

			if (i >= j) {

				break;

			}

			this.swap(array, i, j);

		}

		this.swap(array, left, j);

		return j;

	}

	private void swap(E[] array, int i, int j) {

		E temp = array[i];

		array[i] = array[j];
		array[j] = temp;

	}

	@FunctionalInterface
	public interface SpecialComparator<O, E> {

		int compare(O o, E e);

	}

	@Override
	public Iterator<E> iterator() {

		return new SimpleIterator();

	}

	private class SimpleIterator implements Iterator<E> {

		private int index;

		@Override
		public boolean hasNext() {

			return this.index < size;

		}

		@Override
		public E next() {

			return contents[this.index++];

		}

	}

}
